home *** CD-ROM | disk | FTP | other *** search
/ Libris Britannia 4 / science library(b).zip / science library(b) / PROGRAMM / PROGEDIT / H402.ZIP / ELV17SRC.ZIP / EX.C < prev    next >
C/C++ Source or Header  |  1992-12-30  |  18KB  |  771 lines

  1. /* ex.c */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains the code for reading ex commands. */
  12.  
  13. #include "config.h"
  14. #include "ctype.h"
  15. #include "vi.h"
  16.  
  17. /* This data type is used to describe the possible argument combinations */
  18. typedef short ARGT;
  19. #define FROM    1        /* allow a linespec */
  20. #define    TO    2        /* allow a second linespec */
  21. #define BANG    4        /* allow a ! after the command name */
  22. #define EXTRA    8        /* allow extra args after command name */
  23. #define XFILE    16        /* expand wildcards in extra part */
  24. #define NOSPC    32        /* no spaces allowed in the extra part */
  25. #define    DFLALL    64        /* default file range is 1,$ */
  26. #define DFLNONE    128        /* no default file range */
  27. #define NODFL    256        /* do not default to the current file name */
  28. #define EXRCOK    512        /* can be in a .exrc file */
  29. #define NL    1024        /* if mode!=MODE_EX, then write a newline first */
  30. #define PLUS    2048        /* allow a line number, as in ":e +32 foo" */
  31. #define ZERO    4096        /* allow 0 to be given as a line number */
  32. #define NOBAR    8192        /* treat following '|' chars as normal */
  33. #define FILES    (XFILE + EXTRA)    /* multiple extra files allowed */
  34. #define WORD1    (EXTRA + NOSPC)    /* one extra word allowed */
  35. #define FILE1    (FILES + NOSPC)    /* 1 file allowed, defaults to current file */
  36. #define NAMEDF    (FILE1 + NODFL)    /* 1 file allowed, defaults to "" */
  37. #define NAMEDFS    (FILES + NODFL)    /* multiple files allowed, default is "" */
  38. #define RANGE    (FROM + TO)    /* range of linespecs allowed */
  39. #define NONE    0        /* no args allowed at all */
  40.  
  41. /* This array maps ex command names to command codes. The order in which
  42.  * command names are listed below is significant -- ambiguous abbreviations
  43.  * are always resolved to be the first possible match.  (e.g. "r" is taken
  44.  * to mean "read", not "rewind", because "read" comes before "rewind")
  45.  */
  46. static struct
  47. {
  48.     char    *name;    /* name of the command */
  49.     CMD    code;    /* enum code of the command */
  50.     void    (*fn)();/* function which executes the command */
  51.     ARGT    argt;    /* command line arguments permitted/needed/used */
  52. }
  53.     cmdnames[] =
  54. {   /*    cmd name    cmd code    function    arguments */
  55.     {"print",    CMD_PRINT,    cmd_print,    RANGE+NL    },
  56.  
  57.     {"append",    CMD_APPEND,    cmd_append,    FROM+ZERO+BANG    },
  58. #ifdef DEBUG
  59.     {"bug",        CMD_DEBUG,    cmd_debug,    RANGE+BANG+EXTRA+NL},
  60. #endif
  61.     {"change",    CMD_CHANGE,    cmd_append,    RANGE+BANG    },
  62.     {"delete",    CMD_DELETE,    cmd_delete,    RANGE+WORD1    },
  63.     {"edit",    CMD_EDIT,    cmd_edit,    BANG+FILE1+PLUS    },
  64.     {"file",    CMD_FILE,    cmd_file,    NAMEDF        },
  65.     {"global",    CMD_GLOBAL,    cmd_global,    RANGE+BANG+EXTRA+DFLALL+NOBAR},
  66.     {"insert",    CMD_INSERT,    cmd_append,    FROM+BANG    },
  67.     {"join",    CMD_INSERT,    cmd_join,    RANGE+BANG    },
  68.     {"k",        CMD_MARK,    cmd_mark,    FROM+WORD1    },
  69.     {"list",    CMD_LIST,    cmd_print,    RANGE+NL    },
  70.     {"move",    CMD_MOVE,    cmd_move,    RANGE+EXTRA    },
  71.     {"next",    CMD_NEXT,    cmd_next,    BANG+NAMEDFS    },
  72.     {"Next",    CMD_PREVIOUS,    cmd_next,    BANG        },
  73.     {"quit",    CMD_QUIT,    cmd_xit,    BANG        },
  74.     {"read",    CMD_READ,    cmd_read,    FROM+ZERO+NAMEDF},
  75.     {"substitute",    CMD_SUBSTITUTE,    cmd_substitute,    RANGE+EXTRA    },
  76.     {"to",        CMD_COPY,    cmd_move,    RANGE+EXTRA    },
  77.     {"undo",    CMD_UNDO,    cmd_undo,    NONE        },
  78.     {"vglobal",    CMD_VGLOBAL,    cmd_global,    RANGE+EXTRA+DFLALL+NOBAR},
  79.     {"write",    CMD_WRITE,    cmd_write,    RANGE+BANG+FILE1+DFLALL},
  80.     {"xit",        CMD_XIT,    cmd_xit,    BANG+NL        },
  81.     {"yank",    CMD_YANK,    cmd_delete,    RANGE+WORD1    },
  82.  
  83.     {"!",        CMD_BANG,    cmd_shell,    EXRCOK+RANGE+NAMEDFS+DFLNONE+NL+NOBAR},
  84.     {"#",        CMD_NUMBER,    cmd_print,    RANGE+NL    },
  85.     {"<",        CMD_SHIFTL,    cmd_shift,    RANGE        },
  86.     {">",        CMD_SHIFTR,    cmd_shift,    RANGE        },
  87.     {"=",        CMD_EQUAL,    cmd_file,    RANGE        },
  88.     {"&",        CMD_SUBAGAIN,    cmd_substitute,    RANGE        },
  89. #ifndef NO_AT
  90.     {"@",        CMD_AT,        cmd_at,        EXTRA        },
  91. #endif
  92.  
  93. #ifndef NO_ABBR
  94.     {"abbreviate",    CMD_ABBR,    cmd_map,    EXRCOK+BANG+EXTRA},
  95. #endif
  96.     {"args",    CMD_ARGS,    cmd_args,    EXRCOK+NAMEDFS    },
  97. #ifndef NO_ERRLIST
  98.     {"cc",        CMD_CC,        cmd_make,    BANG+FILES    },
  99. #endif
  100.     {"cd",        CMD_CD,        cmd_cd,        EXRCOK+BANG+NAMEDF},
  101.     {"copy",    CMD_COPY,    cmd_move,    RANGE+EXTRA    },
  102. #ifndef NO_DIGRAPH
  103.     {"digraph",    CMD_DIGRAPH,    cmd_digraph,    EXRCOK+BANG+EXTRA},
  104. #endif
  105. #ifndef NO_ERRLIST
  106.     {"errlist",    CMD_ERRLIST,    cmd_errlist,    BANG+NAMEDF    },
  107. #endif
  108.     {"ex",        CMD_EDIT,    cmd_edit,    BANG+FILE1    },
  109.     {"mark",    CMD_MARK,    cmd_mark,    FROM+WORD1    },
  110. #ifndef NO_MKEXRC
  111.     {"mkexrc",    CMD_MKEXRC,    cmd_mkexrc,    NAMEDF        },
  112. #endif
  113.     {"number",    CMD_NUMBER,    cmd_print,    RANGE+NL    },
  114. #ifndef NO_TAGSTACK
  115.     {"pop",        CMD_POP,    cmd_pop,    BANG+WORD1    },
  116. #endif
  117.     {"put",        CMD_PUT,    cmd_put,    FROM+ZERO+WORD1    },
  118.     {"set",        CMD_SET,    cmd_set,    EXRCOK+EXTRA    },
  119.     {"shell",    CMD_SHELL,    cmd_shell,    NL        },
  120.     {"source",    CMD_SOURCE,    cmd_source,    EXRCOK+NAMEDF    },
  121. #ifdef SIGTSTP
  122.     {"stop",    CMD_STOP,    cmd_suspend,    NONE        },
  123. #endif
  124.     {"tag",        CMD_TAG,    cmd_tag,    BANG+WORD1    },
  125.     {"version",    CMD_VERSION,    cmd_version,    EXRCOK+NONE    },
  126.     {"visual",    CMD_VISUAL,    cmd_edit,    BANG+NAMEDF    },
  127.     {"wq",        CMD_WQUIT,    cmd_xit,    NL        },
  128.  
  129. #ifdef DEBUG
  130.     {"debug",    CMD_DEBUG,    cmd_debug,    RANGE+BANG+EXTRA+NL},
  131.     {"validate",    CMD_VALIDATE,    cmd_validate,    BANG+NL        },
  132. #endif
  133.     {"chdir",    CMD_CD,        cmd_cd,        EXRCOK+BANG+NAMEDF},
  134. #ifndef NO_COLOR
  135.     {"color",    CMD_COLOR,    cmd_color,    EXRCOK+EXTRA    },
  136. #endif
  137. #ifndef NO_ERRLIST
  138.     {"make",    CMD_MAKE,    cmd_make,    BANG+NAMEDFS    },
  139. #endif
  140.     {"map",        CMD_MAP,    cmd_map,    EXRCOK+BANG+EXTRA},
  141.     {"previous",    CMD_PREVIOUS,    cmd_next,    BANG        },
  142.     {"rewind",    CMD_REWIND,    cmd_next,    BANG        },
  143. #ifdef SIGTSTP
  144.     {"suspend",    CMD_SUSPEND,    cmd_suspend,    NONE        },
  145. #endif
  146.     {"unmap",    CMD_UNMAP,    cmd_map,    EXRCOK+BANG+EXTRA},
  147. #ifndef NO_ABBR
  148.     {"unabbreviate",CMD_UNABBR,    cmd_map,    EXRCOK+WORD1    },
  149. #endif
  150.  
  151.     {(char *)0}
  152. };
  153.  
  154.  
  155. /* This function parses a search pattern - given a pointer to a / or ?,
  156.  * it replaces the ending / or ? with a \0, and returns a pointer to the
  157.  * stuff that came after the pattern.
  158.  */
  159. char    *parseptrn(ptrn)
  160.     REG char    *ptrn;
  161. {
  162.     REG char     *scan;
  163.  
  164.     for (scan = ptrn + 1;
  165.          *scan && *scan != *ptrn;
  166.          scan++)
  167.     {
  168.         /* allow backslashed versions of / and ? in the pattern */
  169.         if (*scan == '\\' && scan[1] != '\0')
  170.         {
  171.             scan++;
  172.         }
  173.     }
  174.     if (*scan)
  175.     {
  176.         *scan++ = '\0';
  177.     }
  178.  
  179.     return scan;
  180. }
  181.  
  182.  
  183. /* This function parses a line specifier for ex commands */
  184. char *linespec(s, markptr)
  185.     REG char    *s;        /* start of the line specifier */
  186.     MARK        *markptr;    /* where to store the mark's value */
  187. {
  188.     long        num;
  189.     REG char    *t;
  190.  
  191.     /* parse each ;-delimited clause of this linespec */
  192.     do
  193.     {
  194.         /* skip an initial ';', if any */
  195.         if (*s == ';')
  196.         {
  197.             s++;
  198.         }
  199.  
  200.         /* skip leading spaces */
  201.         while (isspace(*s))
  202.         {
  203.             s++;
  204.         }
  205.  
  206.         /* dot means current position */
  207.         if (*s == '.')
  208.         {
  209.             s++;
  210.             *markptr = cursor;
  211.         }
  212.         /* '$' means the last line */
  213.         else if (*s == '$')
  214.         {
  215.             s++;
  216.             *markptr = MARK_LAST;
  217.         }
  218.         /* digit means an absolute line number */
  219.         else if (isdigit(*s))
  220.         {
  221.             for (num = 0; isdigit(*s); s++)
  222.             {
  223.                 num = num * 10 + *s - '0';
  224.             }
  225.             *markptr = MARK_AT_LINE(num);
  226.         }
  227.         /* appostrophe means go to a set mark */
  228.         else if (*s == '\'')
  229.         {
  230.             s++;
  231.             *markptr = m_tomark(cursor, 1L, (int)*s);
  232.             s++;
  233.         }
  234.         /* slash means do a search */
  235.         else if (*s == '/' || *s == '?')
  236.         {
  237.             /* put a '\0' at the end of the search pattern */
  238.             t = parseptrn(s);
  239.  
  240.             /* search for the pattern */
  241.             *markptr &= ~(BLKSIZE - 1);
  242.             if (*s == '/')
  243.             {
  244.                 pfetch(markline(*markptr));
  245.                 if (plen > 0)
  246.                     *markptr += plen - 1;
  247.                 *markptr = m_fsrch(*markptr, s);
  248.             }
  249.             else
  250.             {
  251.                 *markptr = m_bsrch(*markptr, s);
  252.             }
  253.  
  254.             /* adjust command string pointer */
  255.             s = t;
  256.         }
  257.  
  258.         /* if linespec was faulty, quit now */
  259.         if (!*markptr)
  260.         {
  261.             return s;
  262.         }
  263.  
  264.         /* maybe add an offset */
  265.         t = s;
  266.         if (*t == '-' || *t == '+')
  267.         {
  268.             s++;
  269.             for (num = 0; isdigit(*s); s++)
  270.             {
  271.                 num = num * 10 + *s - '0';
  272.             }
  273.             if (num == 0)
  274.             {
  275.                 num = 1;
  276.             }
  277.             *markptr = m_updnto(*markptr, num, *t);
  278.         }
  279.     } while (*s == ';' || *s == '+' || *s == '-');
  280.  
  281.     /* protect against invalid line numbers */
  282.     num = markline(*markptr);
  283.     if (num < 1L || num > nlines)
  284.     {
  285.         msg("Invalid line number -- must be from 1 to %ld", nlines);
  286.         *markptr = MARK_UNSET;
  287.     }
  288.  
  289.     return s;
  290. }
  291.  
  292.  
  293.  
  294. /* This function reads an ex command and executes it. */
  295. void ex()
  296. {
  297.     char        cmdbuf[150];
  298.     REG int        cmdlen;
  299.     static long    oldline;
  300.  
  301.     significant = FALSE;
  302.     oldline = markline(cursor);
  303.  
  304.     while (mode == MODE_EX)
  305.     {
  306.         /* read a line */
  307. #ifdef CRUNCH
  308.         cmdlen = vgets(':', cmdbuf, sizeof(cmdbuf));
  309. #else
  310.         cmdlen = vgets(*o_prompt ? ':' : '\0', cmdbuf, sizeof(cmdbuf));
  311. #endif
  312.         if (cmdlen < 0)
  313.         {
  314.             return;
  315.         }
  316.  
  317.         /* if empty line, assume ".+1" */
  318.         if (cmdlen == 0)
  319.         {
  320.             strcpy(cmdbuf, ".+1");
  321.             qaddch('\r');
  322.             clrtoeol();
  323.         }
  324.         else
  325.         {
  326.             addch('\n');
  327.         }
  328.         refresh();
  329.  
  330.         /* parse & execute the command */
  331.         doexcmd(cmdbuf);
  332.  
  333.         /* handle autoprint */
  334.         if (significant || markline(cursor) != oldline)
  335.         {
  336.             significant = FALSE;
  337.             oldline = markline(cursor);
  338.             if (*o_autoprint && mode == MODE_EX)
  339.             {
  340.                 cmd_print(cursor, cursor, CMD_PRINT, FALSE, "");
  341.             }
  342.         }
  343.     }
  344. }
  345.  
  346. void doexcmd(cmdbuf)
  347.     char        *cmdbuf;    /* string containing an ex command */
  348. {
  349.     REG char    *scan;        /* used to scan thru cmdbuf */
  350.     MARK        frommark;    /* first linespec */
  351.     MARK        tomark;        /* second linespec */
  352.     REG int        cmdlen;        /* length of the command name given */
  353.     CMD        cmd;        /* what command is this? */
  354.     ARGT        argt;        /* argument types for this command */
  355.     short        forceit;    /* bang version of a command? */
  356.     REG int        cmdidx;        /* index of command */
  357.     REG char    *build;        /* used while copying filenames */
  358.     int        iswild;        /* boolean: filenames use wildcards? */
  359.     int        isdfl;        /* using default line ranges? */
  360.     int        didsub;        /* did we substitute file names for % or # */
  361.  
  362.     /* ex commands can't be undone via the shift-U command */
  363.     U_line = 0L;
  364.  
  365.     /* permit extra colons at the start of the line */
  366.     for (; *cmdbuf == ':'; cmdbuf++)
  367.     {
  368.     }
  369.  
  370.     /* ignore command lines that start with a double-quote */
  371.     if (*cmdbuf == '"')
  372.     {
  373.         return;
  374.     }
  375.     scan = cmdbuf;
  376.  
  377.     /* parse the line specifier */
  378.     if (nlines < 1)
  379.     {
  380.         /* no file, so don't allow addresses */
  381.     }
  382.     else if (*scan == '%')
  383.     {
  384.         /* '%' means all lines */
  385.         frommark = MARK_FIRST;
  386.         tomark = MARK_LAST;
  387.         scan++;
  388.     }
  389.     else if (*scan == '0')
  390.     {
  391.         scan++;
  392.         frommark = tomark = (*scan ? MARK_UNSET : MARK_FIRST);
  393.     }
  394.     else
  395.     {
  396.         frommark = cursor;
  397.         scan = linespec(scan, &frommark);
  398.         tomark = frommark;
  399.         if (frommark && *scan == ',')
  400.         {
  401.             scan++;
  402.             scan = linespec(scan, &tomark);
  403.         }
  404.         if (!tomark)
  405.         {
  406.             /* faulty line spec -- fault already described */
  407.             return;
  408.         }
  409.         if (frommark > tomark)
  410.         {
  411.             msg("first address exceeds the second");
  412.             return;
  413.         }
  414.     }
  415.     isdfl = (scan == cmdbuf);
  416.  
  417.     /* skip whitespace */
  418.     while (isspace(*scan))
  419.     {
  420.         scan++;
  421.     }
  422.  
  423.     /* Figure out how long the command name is.  If no command, then the
  424.      * length is 0, which will match the "print" command.
  425.      */ 
  426.     if (!*scan)
  427.     {
  428.         /* if not in ex mode, and both endpoints are at the line,
  429.          * then just move to the start of that line without printing
  430.          */
  431.         if (mode != MODE_EX && frommark == tomark)
  432.         {
  433.             if (tomark != MARK_UNSET)
  434.                 cursor = tomark;
  435.             return;
  436.         }
  437.         cmdlen = 0;
  438.     }
  439.     else if (!isalpha(*scan))
  440.     {
  441.         cmdlen = 1;
  442.     }
  443.     else
  444.     {
  445.         for (cmdlen = 1;
  446.              isalpha(scan[cmdlen]);
  447.              cmdlen++)
  448.         {
  449.         }
  450.     }
  451.  
  452.     /* lookup the command code */
  453.     for (cmdidx = 0;
  454.          cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen);
  455.          cmdidx++)
  456.     {
  457.     }
  458.     argt = cmdnames[cmdidx].argt;
  459.     cmd = cmdnames[cmdidx].code;
  460.     if (cmd == CMD_NULL)
  461.     {
  462.         msg("Unknown command \"%.*s\"", cmdlen, scan);
  463.         return;
  464.     }
  465.  
  466.     /* !!! if the command doesn't have NOBAR set, then replace | with \0 */
  467.  
  468.     /* if the command ended with a bang, set the forceit flag */
  469.     scan += cmdlen;
  470.     if ((argt & BANG) && *scan == '!')
  471.     {
  472.         scan++;
  473.         forceit = 1;
  474.     }
  475.     else
  476.     {
  477.         forceit = 0;
  478.     }
  479.  
  480.     /* skip any more whitespace, to leave scan pointing to arguments */
  481.     while (isspace(*scan))
  482.     {
  483.         scan++;
  484.     }
  485.  
  486.     /* a couple of special cases for filenames */
  487.     if (argt & XFILE)
  488.     {
  489.         /* if names were given, process them */
  490.         if (*scan)
  491.         {
  492.             for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++)
  493.             {
  494.                 switch (*scan)
  495.                 {
  496.                   case '\\':
  497.                     if (scan[1] == '\\' || scan[1] == '%' || scan[1] == '#')
  498.                     {
  499.                         *build++ = *++scan;
  500.                     }
  501.                     else
  502.                     {
  503.                         *build++ = '\\';
  504.                     }
  505.                     break;
  506.  
  507.                   case '%':
  508.                     if (!*origname)
  509.                     {
  510.                         msg("No filename to substitute for %%");
  511.                         return;
  512.                     }
  513.                     strcpy(build, origname);
  514.                     while (*build)
  515.                     {
  516.                         build++;
  517.                     }
  518.                     didsub = TRUE;
  519.                     break;
  520.  
  521.                   case '#':
  522.                     if (!*prevorig)
  523.                     {
  524.                         msg("No filename to substitute for #");
  525.                         return;
  526.                     }
  527.                     strcpy(build, prevorig);
  528.                     while (*build)
  529.                     {
  530.                         build++;
  531.                     }
  532.                     didsub = TRUE;
  533.                     break;
  534.  
  535.                   case '*':
  536.                   case '?':
  537. #if !(MSDOS || TOS)
  538.                   case '[':
  539.                   case '`':
  540.                   case '{': /* } */
  541.                   case '$':
  542.                   case '~':
  543. #endif
  544.                     *build++ = *scan;
  545.                     iswild = TRUE;
  546.                     break;
  547.  
  548.                   default:
  549.                     *build++ = *scan;
  550.                 }
  551.             }
  552.             *build = '\0';
  553.  
  554.             if (cmd == CMD_BANG
  555.              || cmd == CMD_READ && tmpblk.c[0] == '!'
  556.              || cmd == CMD_WRITE && tmpblk.c[0] == '!')
  557.             {
  558.                 if (didsub)
  559.                 {
  560.                     if (mode != MODE_EX)
  561.                     {
  562.                         addch('\n');
  563.                     }
  564.                     addstr(tmpblk.c);
  565.                     addch('\n');
  566.                     exrefresh();
  567.                 }
  568.             }
  569.             else
  570.             {
  571.                 if (iswild && tmpblk.c[0] != '>')
  572.                 {
  573.                     scan = wildcard(tmpblk.c);
  574.                 }
  575.             }
  576.         }
  577.         else /* no names given, maybe assume origname */
  578.         {
  579.             if (!(argt & NODFL))
  580.             {
  581.                 strcpy(tmpblk.c, origname);
  582.             }
  583.             else
  584.             {
  585.                 *tmpblk.c = '\0';
  586.             }
  587.         }
  588.  
  589.         scan = tmpblk.c;
  590.     }
  591.  
  592.     /* bad arguments? */
  593.     if (!(argt & EXRCOK) && nlines < 1L)
  594.     {
  595.         msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC);
  596.         return;
  597.     }
  598.     if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET)
  599.     {
  600.         msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name);
  601.         return;
  602.     }
  603.     if (!(argt & FROM) && frommark != cursor && nlines >= 1L)
  604.     {
  605.         msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name);
  606.         return;
  607.     }
  608.     if (!(argt & TO) && tomark != frommark && nlines >= 1L)
  609.     {
  610.         msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name);
  611.         return;
  612.     }
  613.     if (!(argt & EXTRA) && *scan)
  614.     {
  615.         msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name);
  616.         return;
  617.     }
  618.     if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!')))
  619.     {
  620.         build = scan;
  621. #ifndef CRUNCH
  622.         if ((argt & PLUS) && *build == '+')
  623.         {
  624.             while (*build && !isspace(*build))
  625.             {
  626.                 build++;
  627.             }
  628.             while (*build && isspace(*build))
  629.             {
  630.                 build++;
  631.             }
  632.         }
  633. #endif /* not CRUNCH */
  634.         for (; *build; build++)
  635.         {
  636.             if (isspace(*build))
  637.             {
  638.                 msg("Too many %s to \"%s\" command.",
  639.                     (argt & XFILE) ? "filenames" : "arguments",
  640.                     cmdnames[cmdidx].name);
  641.                 return;
  642.             }
  643.         }
  644.     }
  645.  
  646.     /* some commands have special default ranges */
  647.     if (isdfl && (argt & DFLALL))
  648.     {
  649.         frommark = MARK_FIRST;
  650.         tomark = MARK_LAST;
  651.     }
  652.     else if (isdfl && (argt & DFLNONE))
  653.     {
  654.         frommark = tomark = 0L;
  655.     }
  656.  
  657.     /* write a newline if called from visual mode */
  658.     if ((argt & NL) && mode != MODE_EX && !exwrote)
  659.     {
  660.         addch('\n');
  661.         exrefresh();
  662.     }
  663.  
  664.     /* act on the command */
  665.     (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan);
  666. }
  667.  
  668.  
  669. /* This function executes EX commands from a file.  It returns 1 normally, or
  670.  * 0 if the file could not be opened for reading.
  671.  */
  672. int doexrc(filename)
  673.     char    *filename;    /* name of a ".exrc" file */
  674. {
  675.     int    fd;        /* file descriptor */
  676.     int    len;        /* length of the ".exrc" file */
  677.  
  678. #ifdef CRUNCH
  679.     /* small address space - we need to conserve space */
  680.  
  681.     /* !!! kludge: we use U_text as the buffer.  This has the side-effect
  682.      * of interfering with the shift-U visual command.  Disable shift-U.
  683.      */
  684.     U_line = 0L;
  685. #else
  686. # if TINYSTACK
  687. #  if TOS || MINT
  688.     /* small stack, but big heap.  Allocate buffer from heap */
  689.     char    *U_text = (char *)malloc(4096);
  690.     if (!U_text)
  691.     {
  692.         return 0;
  693.     }
  694. #  else
  695.     /* small stack - we need to conserve space */
  696.  
  697.     /* !!! kludge: we use U_text as the buffer.  This has the side-effect
  698.      * of interfering with the shift-U visual command.  Disable shift-U.
  699.      */
  700.     U_line = 0L;
  701. #  endif
  702. # else
  703.     /* This is how we would *like* to do it -- with a large buffer on the
  704.      * stack, so we can handle large .exrc files and also recursion.
  705.      */
  706.     char    U_text[4096];
  707. # endif
  708. #endif
  709.  
  710.     /* open the file, read it, and close */
  711.     fd = open(filename, O_RDONLY);
  712.     if (fd < 0)
  713.     {
  714. #if TINYSTACK && (TOS || MINT)
  715.         free(U_text);
  716. #endif
  717.         return 0;
  718.     }
  719. #if TINYSTACK && (TOS || MINT)
  720.     len = tread(fd, U_text, 4096);
  721. #else
  722.     len = tread(fd, U_text, sizeof U_text);
  723. #endif
  724.     close(fd);
  725.  
  726.     /* execute the string */
  727.     exstring(U_text, len, ctrl('V'));
  728.  
  729. #if TINYSTACK && (TOS || MINT)
  730.     free(U_text);
  731. #endif
  732.     return 1;
  733. }
  734.  
  735. /* This function executes EX commands from a string.  The commands may be
  736.  * separated by newlines or by | characters.  It also handles quoting.
  737.  * Each individual command is limited to 132 bytes, but the total string
  738.  * may be longer.
  739.  */
  740. void exstring(buf, len, qchar)
  741.     char    *buf;    /* the commands to execute */
  742.     int    len;    /* the length of the string */
  743.     int    qchar;    /* the quote character -- ^V for file, or \ for kbd */
  744. {
  745.     char    single[133];    /* a single command */
  746.     char    *src, *dest;
  747.     int    i;
  748.  
  749.     /* find & do each command */
  750.     for (src = buf; src < &buf[len]; src++)
  751.     {
  752.         /* Copy a single command into single[].  Convert any quoted |
  753.          * into a normal |, and stop at a newline or unquoted |.
  754.          */
  755.         for (dest = single, i = 0;
  756.              i < 132 && src < &buf[len] && *src != '\n' && *src != '|';
  757.              src++, i++)
  758.         {
  759.             if (src[0] == qchar && src[1] == '|')
  760.             {
  761.                 src++;
  762.             }
  763.             *dest++ = *src;
  764.         }
  765.         *dest = '\0';
  766.  
  767.         /* do it */
  768.         doexcmd(single);
  769.     }
  770. }
  771.